home *** CD-ROM | disk | FTP | other *** search
/ Download Now 8 / Download Now V8.iso / Program / InternetTools / ApacheWebServer1.3.6 / apache_1_3_6_win32.exe / _SETUP.1 / htpasswd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-19  |  14.8 KB  |  563 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /******************************************************************************
  59.  ******************************************************************************
  60.  * NOTE! This program is not safe as a setuid executable!  Do not make it
  61.  * setuid!
  62.  ******************************************************************************
  63.  *****************************************************************************/
  64. /*
  65.  * htpasswd.c: simple program for manipulating password file for
  66.  * the Apache HTTP server
  67.  * 
  68.  * Originally by Rob McCool
  69.  *
  70.  * Exit values:
  71.  *  0: Success
  72.  *  1: Failure; file access/permission problem
  73.  *  2: Failure; command line syntax problem (usage message issued)
  74.  *  3: Failure; password verification failure
  75.  *  4: Failure; operation interrupted (such as with CTRL/C)
  76.  *  5: Failure; buffer would overflow (username, filename, or computed
  77.  *     record too long)
  78.  */
  79.  
  80. #include "ap_config.h"
  81. #include <sys/types.h>
  82. #include <signal.h>
  83. #include <errno.h>
  84. #include "ap.h"
  85. #include "ap_md5.h"
  86.  
  87. #ifdef WIN32
  88. #include <conio.h>
  89. #include "../os/win32/getopt.h"
  90. #define unlink _unlink
  91. #endif
  92.  
  93. #ifndef CHARSET_EBCDIC
  94. #define LF 10
  95. #define CR 13
  96. #else /*CHARSET_EBCDIC*/
  97. #define LF '\n'
  98. #define CR '\r'
  99. #endif /*CHARSET_EBCDIC*/
  100.  
  101. #define MAX_STRING_LEN 256
  102. #define ALG_CRYPT 1
  103. #define ALG_APMD5 2
  104.  
  105. #define ERR_FILEPERM 1
  106. #define ERR_SYNTAX 2
  107. #define ERR_PWMISMATCH 3
  108. #define ERR_INTERRUPTED 4
  109. #define ERR_OVERFLOW 5
  110.  
  111. /*
  112.  * This needs to be declared statically so the signal handler can
  113.  * access it.
  114.  */
  115. static char *tempfilename;
  116.  
  117. /*
  118.  * Duplicate a string into memory malloc()ed for it.
  119.  */
  120. static char *strd(char *s)
  121. {
  122.     char *d;
  123.  
  124.     d = (char *) malloc(strlen(s) + 1);
  125.     strcpy(d, s);
  126.     return (d);
  127. }
  128.  
  129. static int getline(char *s, int n, FILE *f)
  130. {
  131.     register int i = 0;
  132.  
  133.     while (1) {
  134.     s[i] = (char) fgetc(f);
  135.  
  136.     if (s[i] == CR) {
  137.         s[i] = fgetc(f);
  138.     }
  139.  
  140.     if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
  141.         s[i] = '\0';
  142.         return (feof(f) ? 1 : 0);
  143.     }
  144.     ++i;
  145.     }
  146. }
  147.  
  148. static void putline(FILE *f, char *l)
  149. {
  150.     int x;
  151.  
  152.     for (x = 0; l[x]; x++) {
  153.     fputc(l[x], f);
  154.     }
  155.     fputc('\n', f);
  156. }
  157.  
  158.  
  159. /* From local_passwd.c (C) Regents of Univ. of California blah blah */
  160. static unsigned char itoa64[] =    /* 0 ... 63 => ascii - 64 */
  161.     "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  162.  
  163. static void to64(register char *s, register long v, register int n)
  164. {
  165.     while (--n >= 0) {
  166.     *s++ = itoa64[v & 0x3f];
  167.     v >>= 6;
  168.     }
  169. }
  170.  
  171. #ifdef MPE
  172. /*
  173.  * MPE lacks getpass() and a way to suppress stdin echo.  So for now, just
  174.  * issue the prompt and read the results with echo.  (Ugh).
  175.  */
  176.  
  177. static char *getpass(const char *prompt)
  178. {
  179.     static char password[81];
  180.  
  181.     fputs(prompt, stderr);
  182.     gets((char *) &password);
  183.  
  184.     if (strlen((char *) &password) > 8) {
  185.     password[8] = '\0';
  186.     }
  187.  
  188.     return (char *) &password;
  189. }
  190.  
  191. #endif
  192.  
  193. #ifdef WIN32
  194. /*
  195.  * Windows lacks getpass().  So we'll re-implement it here.
  196.  */
  197.  
  198. static char *getpass(const char *prompt)
  199. {
  200.     static char password[81];
  201.     int n = 0;
  202.  
  203.     fputs(prompt, stderr);
  204.     
  205.     while ((password[n] = _getch()) != '\r') {
  206.         if (password[n] >= ' ' && password[n] <= '~') {
  207.             n++;
  208.             printf("*");
  209.         }
  210.     else {
  211.             printf("\n");
  212.             fputs(prompt, stderr);
  213.             n = 0;
  214.         }
  215.     }
  216.  
  217.     password[n] = '\0';
  218.     printf("\n");
  219.  
  220.     if (n > 8) {
  221.         password[8] = '\0';
  222.     }
  223.  
  224.     return (char *) &password;
  225. }
  226. #endif
  227.  
  228. /*
  229.  * Make a password record from the given information.  A zero return
  230.  * indicates success; failure means that the output buffer contains an
  231.  * error message instead.
  232.  */
  233. static int mkrecord(char *user, char *record, size_t rlen, int alg)
  234. {
  235.     char *pw;
  236.     char cpw[120];
  237.     char salt[9];
  238.  
  239.     pw = strd((char *) getpass("New password: "));
  240.     if (strcmp(pw, (char *) getpass("Re-type new password: "))) {
  241.     ap_cpystrn(record, "password verification error", (rlen - 1));
  242.     return ERR_PWMISMATCH;
  243.     }
  244.     (void) srand((int) time((time_t *) NULL));
  245.     to64(&salt[0], rand(), 8);
  246.     salt[8] = '\0';
  247.  
  248.     switch (alg) {
  249.     case ALG_APMD5:
  250.     ap_MD5Encode(pw, salt, cpw, sizeof(cpw));
  251.     break;
  252.     case ALG_CRYPT:
  253.     ap_cpystrn(cpw, (char *)crypt(pw, salt), sizeof(cpw) - 1);
  254.     break;
  255.     }
  256.     /*
  257.      * Now that we have the smashed password, we don't need the
  258.      * plaintext one any more.
  259.      */
  260.     free(pw);
  261.     /*
  262.      * Check to see if the buffer is large enough to hold the username,
  263.      * hash, and delimiters.
  264.      */
  265.     if ((strlen(user) + 1 + strlen(cpw)) > (rlen - 1)) {
  266.     ap_cpystrn(record, "resultant record too long", (rlen - 1));
  267.     return ERR_OVERFLOW;
  268.     }
  269.     strcpy(record, user);
  270.     strcat(record, ":");
  271.     strcat(record, cpw);
  272.     return 0;
  273. }
  274.  
  275. static int usage(void)
  276. {
  277.     fprintf(stderr, "Usage: htpasswd [-cm] passwordfile username\n");
  278.     fprintf(stderr, "The -c flag creates a new file.\n");
  279.     fprintf(stderr, "The -m flag forces MD5 encryption of the password.\n");
  280.     fprintf(stderr, "On Windows systems the -m flag is used by default.\n");
  281.     return ERR_SYNTAX;
  282. }
  283.  
  284. static void interrupted(void)
  285. {
  286.     fprintf(stderr, "Interrupted.\n");
  287.     if (tempfilename != NULL) {
  288.     unlink(tempfilename);
  289.     }
  290.     exit(ERR_INTERRUPTED);
  291. }
  292.  
  293. /*
  294.  * Check to see if the specified file can be opened for the given
  295.  * access.
  296.  */
  297. static int accessible(char *fname, char *mode)
  298. {
  299.     FILE *s;
  300.  
  301.     s = fopen(fname, mode);
  302.     if (s == NULL) {
  303.     return 0;
  304.     }
  305.     fclose(s);
  306.     return 1;
  307. }
  308.  
  309. /*
  310.  * Return true if a file is readable.
  311.  */
  312. static int readable(char *fname)
  313. {
  314.     return accessible(fname, "r");
  315. }
  316.  
  317. /*
  318.  * Return true if the specified file can be opened for write access.
  319.  */
  320. static int writable(char *fname)
  321. {
  322.     return accessible(fname, "a");
  323. }
  324.  
  325. /*
  326.  * Return true if the named file exists, regardless of permissions.
  327.  */
  328. static int exists(char *fname)
  329. {
  330. #ifdef WIN32
  331.     struct _stat sbuf;
  332. #else
  333.     struct stat sbuf;
  334. #endif
  335.     int check;
  336.  
  337. #ifdef WIN32
  338.     check = _stat(fname, &sbuf);
  339. #else
  340.     check = stat(fname, &sbuf);
  341. #endif
  342.     return ((check == -1) && (errno == ENOENT)) ? 0 : 1;
  343. }
  344.  
  345. /*
  346.  * Copy from the current position of one file to the current position
  347.  * of another.
  348.  */
  349. static void copy_file(FILE *target, FILE *source)
  350. {
  351.     static char line[MAX_STRING_LEN];
  352.  
  353.     while (fgets(line, sizeof(line), source) != NULL) {
  354.     fputs(line, target);
  355.     }
  356. }
  357.  
  358. /*
  359.  * Let's do it.  We end up doing a lot of file opening and closing,
  360.  * but what do we care?  This application isn't run constantly.
  361.  */
  362. int main(int argc, char *argv[])
  363. {
  364.     FILE *ftemp = NULL;
  365.     FILE *fpw = NULL;
  366.     char user[MAX_STRING_LEN];
  367.     char record[MAX_STRING_LEN];
  368.     char line[MAX_STRING_LEN];
  369.     char pwfilename[MAX_STRING_LEN];
  370.     char *arg;
  371.     int found = 0;
  372.     int alg = ALG_CRYPT;
  373.     int newfile = 0;
  374.     int i;
  375.  
  376.     tempfilename = NULL;
  377.     signal(SIGINT, (void (*)(int)) interrupted);
  378.  
  379.     /*
  380.      * Preliminary check to make sure they provided at least
  381.      * three arguments, we'll do better argument checking as 
  382.      * we parse the command line.
  383.      */
  384.     if (argc < 3) {
  385.     return usage();
  386.     }
  387.  
  388.     /*
  389.      * Go through the argument list and pick out any options.  They
  390.      * have to precede any other arguments.
  391.      */
  392.     for (i = 1; i < argc; i++) {
  393.     arg = argv[i];
  394.     if (*arg != '-') {
  395.         break;
  396.     }
  397.     while (*++arg != '\0') {
  398.         if (*arg == 'c') {
  399.         newfile++;
  400.         }
  401.         else if (*arg == 'm') {
  402.         alg = ALG_APMD5;
  403.         }
  404.         else {
  405.         return usage();
  406.         }
  407.     }
  408.     }
  409.  
  410.     /*
  411.      * Make sure we still have exactly two arguments left (the filename
  412.      * and the username).
  413.      */
  414.     if ((argc - i) != 2) {
  415.     return usage();
  416.     }
  417.     if (strlen(argv[i]) > (sizeof(pwfilename) - 1)) {
  418.     fprintf(stderr, "%s: filename too long\n", argv[0]);
  419.     return ERR_OVERFLOW;
  420.     }
  421.     strcpy(pwfilename, argv[i]);
  422.     if (strlen(argv[i + 1]) > (sizeof(user) - 1)) {
  423.     fprintf(stderr, "%s: username too long\n", argv[0]);
  424.     return ERR_OVERFLOW;
  425.     }
  426.     strcpy(user, argv[i + 1]);
  427.  
  428. #ifdef WIN32
  429.     if (alg == ALG_CRYPT) {
  430.     alg = ALG_APMD5;
  431.     fprintf(stderr, "Automatically using MD5 format on Windows.\n");
  432.     }
  433. #endif
  434.  
  435.     /*
  436.      * Verify that the file exists if -c was omitted.  We give a special
  437.      * message if it doesn't.
  438.      */
  439.     if ((! newfile) && (! exists(pwfilename))) {
  440.     fprintf(stderr, "%s: cannot modify file %s; use '-c' to create it\n",
  441.         argv[0], pwfilename);
  442.     perror("fopen");
  443.     exit(ERR_FILEPERM);
  444.     }
  445.     /*
  446.      * Verify that we can read the existing file in the case of an update
  447.      * to it (rather than creation of a new one).
  448.      */
  449.     if ((! newfile) && (! readable(pwfilename))) {
  450.     fprintf(stderr, "%s: cannot open file %s for read access\n",
  451.         argv[0], pwfilename);
  452.     perror("fopen");
  453.     exit(ERR_FILEPERM);
  454.     }
  455.     /*
  456.      * Now check to see if we can preserve an existing file in case
  457.      * of password verification errors on a -c operation.
  458.      */
  459.     if (newfile && exists(pwfilename) && (! readable(pwfilename))) {
  460.     fprintf(stderr, "%s: cannot open file %s for read access\n"
  461.         "%s: existing auth data would be lost on password mismatch",
  462.         argv[0], pwfilename, argv[0]);
  463.     perror("fopen");
  464.     exit(ERR_FILEPERM);
  465.     }
  466.     /*
  467.      * Now verify that the file is writable!
  468.      */
  469.     if (! writable(pwfilename)) {
  470.     fprintf(stderr, "%s: cannot open file %s for write access\n",
  471.         argv[0], pwfilename);
  472.     perror("fopen");
  473.     exit(ERR_FILEPERM);
  474.     }
  475.  
  476.     /*
  477.      * All the file access checks have been made.  Time to go to work;
  478.      * try to create the record for the username in question.  If that
  479.      * fails, there's no need to waste any time on file manipulations.
  480.      * Any error message text is returned in the record buffer, since
  481.      * the mkrecord() routine doesn't have access to argv[].
  482.      */
  483.     if ((i = mkrecord(user, record, sizeof(record) - 1, alg)) != 0) {
  484.     fprintf(stderr, "%s: %s\n", argv[0], record);
  485.     exit(i);
  486.     }
  487.  
  488.     /*
  489.      * We can access the files the right way, and we have a record
  490.      * to add or update.  Let's do it..
  491.      */
  492.     tempfilename = tmpnam(NULL);
  493.     ftemp = fopen(tempfilename, "w+");
  494.     if (ftemp == NULL) {
  495.     fprintf(stderr, "%s: unable to create temporary file\n", argv[0]);
  496.     perror("fopen");
  497.     exit(ERR_FILEPERM);
  498.     }
  499.     /*
  500.      * If we're not creating a new file, copy records from the existing
  501.      * one to the temporary file until we find the specified user.
  502.      */
  503.     if (! newfile) {
  504.     char scratch[MAX_STRING_LEN];
  505.  
  506.     fpw = fopen(pwfilename, "r");
  507.     while (! (getline(line, sizeof(line), fpw))) {
  508.         char *colon;
  509.  
  510.         if ((line[0] == '#') || (line[0] == '\0')) {
  511.         putline(ftemp, line);
  512.         continue;
  513.         }
  514.         strcpy(scratch, line);
  515.         /*
  516.          * See if this is our user.
  517.          */
  518.         colon = strchr(scratch, ':');
  519.         if (colon != NULL) {
  520.         *colon = '\0';
  521.         }
  522.         if (strcmp(user, scratch) != 0) {
  523.         putline(ftemp, line);
  524.         continue;
  525.         }
  526.         found++;
  527.         break;
  528.     }
  529.     }
  530.     if (found) {
  531.     fprintf(stderr, "Updating ");
  532.     }
  533.     else {
  534.     fprintf(stderr, "Adding ");
  535.     }
  536.     fprintf(stderr, "password for user %s\n", user);
  537.     /*
  538.      * Now add the user record we created.
  539.      */
  540.     putline(ftemp, record);
  541.     /*
  542.      * If we're updating an existing file, there may be additional
  543.      * records beyond the one we're updating, so copy them.
  544.      */
  545.     if (! newfile) {
  546.     copy_file(ftemp, fpw);
  547.     fclose(fpw);
  548.     }
  549.     /*
  550.      * The temporary file now contains the information that should be
  551.      * in the actual password file.  Close the open files, re-open them
  552.      * in the appropriate mode, and copy them file to the real one.
  553.      */
  554.     fclose(ftemp);
  555.     fpw = fopen(pwfilename, "w+");
  556.     ftemp = fopen(tempfilename, "r");
  557.     copy_file(fpw, ftemp);
  558.     fclose(fpw);
  559.     fclose(ftemp);
  560.     unlink(tempfilename);
  561.     return 0;
  562. }
  563.